
namespace Behemoth.SharePoint.Logging {

    using System;
    using System.Configuration;
    using System.Collections.Generic;
    using System.Text;
    using System.Reflection;

    using System.Runtime.InteropServices;
    using System.Diagnostics;

    using Microsoft.SharePoint;
    using Microsoft.SharePoint.Administration;

    using Behemoth.SharePoint.Properties;

    public class ULSTraceLogging {

        private static ULSTraceLogging _defaultProvider;
        /// <summary>
        /// Returns a singleton instance of ULSTraceLogging that can be shared by
        /// multiple callers. This instance will have the default settings.
        /// </summary>
        public static ULSTraceLogging Default {
            get {
                if (_defaultProvider == null)
                    _defaultProvider = new ULSTraceLogging();
                return _defaultProvider;
            }
        }

        public ULSTraceLogging() { }

        private static Settings settings = new Settings();

        private bool _enableAutoRegister = true;
        public bool EnableAutoRegister {
            get {
                return _enableAutoRegister;
            }
            set {
                _enableAutoRegister = value;
            }
        }

        private string _assemblyName;
        public string AssemblyName {
            get {
                if (string.IsNullOrEmpty(_assemblyName)) {
                    _assemblyName = Assembly.GetCallingAssembly().FullName;
                    int pos = _assemblyName.IndexOf(',');
                    if (pos >= 0)
                        _assemblyName = _assemblyName.Substring(0, pos);
                }
                return _assemblyName;
            }
        }

        // reserved for future use
        public string ExeName {
            get {
                return string.Empty;
            }
        }

        private TraceSeverity _defaultLevel = (TraceSeverity)Enum.Parse(typeof(TraceSeverity), settings.ULSDefaultLevel, true);
        public TraceSeverity DefaultLevel {
            get {
                return _defaultLevel;
            }
            set {
                _defaultLevel = value;
            }
        }

        private string _defaultProduct = settings.ULSDefaultProduct;
        public string DefaultProduct {
            get {
                return _defaultProduct;
            }
            set {
                _defaultProduct = value;
            }
        }

        private string _defaultCategory = settings.ULSDefaultCategory;
        public string DefaultCategory {
            get {
                return _defaultCategory;
            }
            set {
                _defaultCategory = value;
            }
        }

        public void Register() {
            if (EnableAutoRegister || TraceProvider.IsRegistered)
                return;
            TraceProvider.RegisterTraceProvider();
        }
        public void Unregister() {
            if (EnableAutoRegister || !TraceProvider.IsRegistered)
                return;
            TraceProvider.UnregisterTraceProvider();
        }

        private void WriteTrace(uint tag, TraceSeverity level, Guid correlation, string assembly, string product, string category, string message) {
            if (EnableAutoRegister && !TraceProvider.IsRegistered)
                TraceProvider.RegisterTraceProvider();
            TraceProvider.WriteTrace(tag, level, correlation, assembly, product, category, message);
            if (EnableAutoRegister && TraceProvider.IsRegistered)
                TraceProvider.UnregisterTraceProvider();
        }

        public void Write(string message) {
            Write(message, _defaultLevel);
        }
        public void Write(string message, TraceSeverity level) {
            Write(message, _defaultLevel, _defaultProduct);
        }
        public void Write(string message, TraceSeverity level, string product) {
            Write(message, _defaultLevel, _defaultProduct, _defaultCategory);
        }
        public void Write(string message, TraceSeverity level, string product, string category) {
            this.WriteTrace(0, _defaultLevel, Guid.Empty, this.AssemblyName, product, category, message);
        }

        public void Write(Exception ex) {
            Write(ex, _defaultProduct, _defaultCategory);
        }
        public void Write(Exception ex, string product) {
            Write(ex, product, _defaultCategory);
        }
        public void Write(Exception ex, string product, string category) {
            string message = BuildErrorMessage(ex);
            TraceSeverity level = TraceSeverity.Exception;
            this.WriteTrace(0, level, Guid.Empty, this.AssemblyName, product, category, message);
        }

        protected string BuildErrorMessage(Exception ex) {
            return BuildErrorMessage(ex, true);
        }
        protected string BuildErrorMessage(Exception ex, bool usePadHeader) {
            if (ex == null)
                return string.Empty;
            string pad = usePadHeader ? settings.ULSExceptionPad + " " : string.Empty;
            string exType = ex.GetType().FullName;
            string exTypeAndSrc = string.IsNullOrEmpty(ex.Source) ? exType : string.Format("{0} in {1}", exType, ex.Source);
            string errorText = string.Format("{0}{1}: {2} {3}", pad, exTypeAndSrc, ex.Message, ex.StackTrace);
            if (ex.InnerException != null)
                errorText += BuildErrorMessage(ex.InnerException, false);
            return errorText;
        }

        public void AuditSPEvent(SPWeb web, SPAuditEventType eventType, string xmlData) {
            AuditSPEvent(web, eventType, xmlData, _defaultProduct);
        }
        public void AuditSPEvent(SPWeb web, SPAuditEventType eventType, string xmlData, string product) {
            try {
                web.Audit.WriteAuditEvent(eventType, product, xmlData);
            } catch (Exception ex) {
                string msg = string.Format("Unable to write event type '{1}' to audit log for web '{0}'. Data: {2}", web.Url, eventType.ToString(), xmlData);
                Exception ex2 = new Exception(msg, ex);
                this.Write(ex, product, settings.ULSCategoryAuditing);
            }
        }

        /// <summary>
        /// This method is depreacted. Instead use SharePointTraceListener.AddListener()
        /// to attach SharePoint logs to the Debug and/or Trace listeners collections, then
        /// call Debug.Assert or Debug.Write as desired.
        /// </summary>
        /// <param name="condition"></param>
        /// <param name="message"></param>
        /// <param name="detail"></param>
        public void Assert(bool condition, string message, string detail) {
            Assert(condition, message, detail, _defaultProduct);
        }
        public void Assert(bool condition, string message, string detail, string product) {
            Debug.Assert(condition, message, detail);
            TraceSeverity level = (condition) ? TraceSeverity.Verbose : TraceSeverity.Assert;
            string msg = string.Format("Assertion is {0}: {1} {2}", condition.ToString(), message, detail);
            this.Write(message, TraceSeverity.Assert, product, settings.ULSCategoryDebugging);
        }

        public void Log(object sender, LoggingEventArgs e) {
            if (e.Exception != null)
                Write(e.Exception, e.Product, e.Category);
            else
                Write(e.Message, e.Severity, e.Product, e.Category);
        }

    } // class

} // namespace
